package com.transfer.http.download.singleThread;

import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.transfer.Scheduler.TransferScheduler;
import com.transfer.common.TaskState;
import com.transfer.common.TaskStateResult;
import com.transfer.common.VerifyMD5Task;
import com.transfer.conf.FileTransferConfiguration;
import com.transfer.http.download.taskInterface.FileDownloadTask;
import com.transfer.http.download.taskInterface.FileSliceDownloadTask;
import com.transfer.http.download.taskInterface.TaskControlInterface;

public class FileDownloadSingleTask extends FileDownloadTask implements TaskControlInterface {
  private static final Logger log = LoggerFactory.getLogger(FileDownloadSingleTask.class);
  private FileSliceDownloadTask fileSliceDownloadTask;
  private VerifyMD5Task verifyMD5Task;
  private TaskStateResult taskStateResult;// 任务状态结果：（任务状态，百分比）
  private TaskState taskState = TaskState.WAITING;// 任务状态

  public FileDownloadSingleTask(long taskId, String urlStr, String fileStorageDir, String MD5) {
    super(taskId, urlStr, fileStorageDir, MD5);
  }

  public FileDownloadSingleTask(long taskId, String urlStr, String MD5) {
    super(taskId, urlStr, FileTransferConfiguration.defaultFileStorageDir, MD5);
  }

  public void work() {
    boolean success = doTask();
    if (success) {
      this.taskState = TaskState.SUCCESS;
      log.info("taskId： " + taskId + " 传输文件成功！");
    } else {
      this.taskState = TaskState.FAILURE;
      log.info("taskId： " + taskId + " 传输文件失败！");
    }

  }

  public boolean doTask() {
    this.taskState = TaskState.PREPARING;
    boolean initSuccess = init();
    if (!initSuccess)
      return false;
    CountDownLatch latch = new CountDownLatch(1);
    boolean sumitSuccess = sliceAndSumitTask(latch);
    if (!sumitSuccess)
      return false;
    this.taskState = TaskState.PROGRESSING;
    boolean reNameSuccess = reNameFile(latch);
    if (!reNameSuccess)
      return false;
    boolean verifySuccess = verifyMD5();
    if (!verifySuccess)
      return false;
    return true;
  }

  public boolean init() {
    this.taskStateResult = new TaskStateResult(this.taskState, "%"+this.computeProgress());
    initFileName(urlStr);
    try {
      this.downloadURL = new URL(urlStr);
    } catch (MalformedURLException e) {
      log.info("创建URL对象失败： " + e);
      return false;
    }
    return true;
  }

  private void initFileName(String urlStr) {
    this.fileName =
        urlStr.substring(urlStr.lastIndexOf("/") + 1,
            urlStr.lastIndexOf("?") > 0 ? urlStr.lastIndexOf("?") : urlStr.length());
    if ("".equalsIgnoreCase(this.fileName)) {
      this.fileName = UUID.randomUUID().toString();
    }
  }

  public boolean sliceAndSumitTask(CountDownLatch latch) {
    statusError = false;
    long contentLength = getContentLength();
    if (contentLength == 0)
      return false;
    this.fileLength = contentLength;
    System.out.println("contentLength: " + contentLength);
    long startPos = getBreakpointPerThread();
    System.out.println("startPos: " + startPos);
    if (startPos == -1)
      return false;
    FileSliceDownloadTask runnableTask =
        new FileSliceDownloadTask(this, latch, 0, startPos, contentLength);
    fileSliceDownloadTask = runnableTask;
    TransferScheduler.getFixedExecutor().submit(runnableTask);
    System.out.println("startPos: " + startPos);
    return true;
  }

  private long getContentLength() {
    HttpURLConnection con;
    try {
      con = (HttpURLConnection) this.downloadURL.openConnection();
      // setHeader(con);
      return con.getContentLength();
    } catch (IOException e) {
      log.info("用http协议从远程服务器获取文件长度时发生异常： " + e);
    }
    return 0;
  }

  public static boolean createDir(String destDirName) {
    File dir = new File(destDirName);
    if (dir.exists()) {
      System.out.println("创建目录" + destDirName + "失败，目标目录已经存在");
      return false;
    }
    if (!destDirName.endsWith(File.separator)) {
      destDirName = destDirName + File.separator;
    }
    // 创建目录
    if (dir.mkdirs()) {
      System.out.println("创建目录" + destDirName + "成功！");
      return true;
    } else {
      System.out.println("创建目录" + destDirName + "失败！");
      return false;
    }
  }

  private long getBreakpointPerThread() {
    long startPos = 0;
    boolean createDirSuccess = createDir(fileStorageDir);
    /*
     * if(!createDirSuccess) return -1;
     */
    System.out.println(fileStorageDir);
    File tempFileDir = new File(fileStorageDir);
    File[] files = tempFileDir.listFiles();
    System.out.println("files.length: " + files.length);
    if (files.length > 1) {
      log.info("error： TaskId: " + this.taskId + " 单线程下载任务的下载目录下有多个文件！");
      return -1;
    } else if (files.length == 1) {
      String tempFileName = files[0].getName();
      if (tempFileName != null && files[0].length() > 0 && tempFileName.startsWith(fileName + "_")) {
        startPos = files[0].length();
      }
    }
    return startPos;
  }

  public boolean reNameFile(CountDownLatch latch) {
    try {
      latch.await();
      this.taskState = TaskState.MERGING;
      File file = this.fileSliceDownloadTask.getTempFile();
      if (file.exists()) {
        file.renameTo(new File(fileStorageDir + fileName));
        return true;
      }
    } catch (InterruptedException e) {
      log.info("taskId: " + taskId + "; 文件下载任务等待完成时线程中断：  " + e);
    }
    return false;
  }

  public boolean verifyMD5() {
    boolean success = false;
    this.verifyMD5Task = new VerifyMD5Task(this.fileStorageDir + this.fileName, this.MD5);
    Future<Boolean> future = TransferScheduler.getFixedExecutor().submit(verifyMD5Task);
    this.taskState = TaskState.VERIFING;
    try {
      success = future.get();
    } catch (InterruptedException e1) {
      log.info("taskId: " + taskId + "; 文件下载任务等待合并完成时线程中断：  " + e1);
    } catch (ExecutionException e1) {
      log.info("taskId: " + taskId + "; 文件下载任务等待合并完成时线程中断：  " + e1);
    }
    return success;
  }

  public FileSliceDownloadTask getFileSliceDownloadTask() {
    return fileSliceDownloadTask;
  }

  public void setFileSliceDownloadTask(FileSliceDownloadTask fileSliceDownloadTask) {
    this.fileSliceDownloadTask = fileSliceDownloadTask;
  }

  private int computeProgress() {
    int progress = 0;
    long startPos = this.fileSliceDownloadTask.getStartPos();
    long endPos = this.fileSliceDownloadTask.getEndPos();
    progress = (int) (startPos / endPos);
    return progress;
  }

  public TaskStateResult getTaskStateResult() {
    this.taskStateResult.setTaskState(taskState);
    this.taskStateResult.setProgressPercentage("%" + computeProgress());
    return taskStateResult;
  }

  public boolean suspendTask() {
    if (this.taskState == TaskState.PROGRESSING)
      this.fileSliceDownloadTask.shutdownRequest();
    else if (this.taskState == TaskState.VERIFING)
      this.verifyMD5Task.shutdownRequest();
    this.taskState = TaskState.SUSPENDING;
    log.info("文件传输任务id：" + this.getTaskId() + "被暂停！ ");
    return true;
  }

  public boolean resumeTask() {
    log.info("文件传输任务id：" + this.getTaskId() + "恢复！ ");
    work();
    return true;
  }

  public boolean deleteTask() {
    if (this.taskState == TaskState.PROGRESSING)
      this.fileSliceDownloadTask.shutdownRequest();
    else if (this.taskState == TaskState.VERIFING)
      this.verifyMD5Task.shutdownRequest();
    clearDir(this.fileStorageDir);
    log.info("文件传输任务id：" + this.getTaskId() + "被删除！ ");
    return true;
  }

  private void clearDir(String fileDir) {
    File file = new File(fileDir);
    File tempFileDir = new File(fileDir);
    File[] files = tempFileDir.listFiles();
    for (int k = 0; k < files.length; k++) {
      files[k].delete();
    }
    tempFileDir.delete();
  }

}
